//============================================================================
// Name        : tp2_p1_.cpp
// Author      : Andrey Bicalho Santos and Clayson Sandro Celes
// Version     :
// Copyright   : Your copyright notice
// Description : tp2 compnat C++, Ansi-style
//============================================================================

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <cstring>
#include "CImg.h"
#include "MazeProblem.h"
#include "Ant.h"

using namespace std;
using namespace cimg_library;

int main(int argc, char** argv) {

	FILE *arquivo;
	char endereco[30];
	int num_formigas,num_iteracoes,refresh_time = 300,ant_energy = 0,aumento_display = 0,ant_memory = 1,
		parametro1_prob,parametro2_prob;
	double taxa_evaporacao = 0,ant_pherom_energy = 0;
	if(argc < 2){
		cout << "Insira o labirinto: (ex: nome_do_arquivo.txt)" << endl; cin >> endereco;
		cout << "Insira o Numero de Formigas: " << endl; cin >> num_formigas;
		cout << "Insira o Numero de Iteracoes: " << endl; cin >> num_iteracoes;
		cout << "Insira a Taxa de Evaporacao:  " << endl; cin >> taxa_evaporacao;
		cout << "Insira a Energia do Feromonio da Formiga: " << endl; cin >> ant_pherom_energy;
		cout << "Insira a Energia da Formiga: " << endl; cin >> ant_energy;
		cout << "Insira o Tamanho da Memoria da Formiga: " << endl; cin >> ant_memory;
		cout << "Insira o parametro 1 da probabilidade de repetir caminho: " <<endl; cin >> parametro1_prob;
		cout << "Insira o parametro 2 da probabilidade de repetir caminho: " <<endl; cin >> parametro2_prob;
		cout << "Insira o Tempo de Atualizacao do Display (ms): " << endl; cin >> refresh_time;
		cout << "Insira o valor de aumento do display (0 - nao muda)" << endl; cin >> aumento_display;
	}
	else{
		strcpy(endereco,argv[1]);
		num_formigas = atoi(argv[2]);
		num_iteracoes = atoi(argv[3]);
		taxa_evaporacao = atof(argv[4]);
		ant_pherom_energy = atof(argv[5]);
		ant_energy = atoi(argv[6]);
		ant_memory = atoi(argv[7]);
		parametro1_prob = atoi(argv[8]);
		parametro2_prob = atoi(argv[9]);
		refresh_time = atoi(argv[10]);
		aumento_display = atoi(argv[11]);
	}

	//cout << endl << taxa_evaporacao << endl << energy << endl;

	arquivo = fopen(endereco,"r");
	if(arquivo == NULL){ cout << "erro ao abrir arquivo de entrada" << endl; return 0;}

	string *mapa; int n_linhas;
	mapa = load_map(arquivo,&n_linhas);

	int **labirinto,**reduced_labirinto;
	Point dim_labirinto,dim_reduced_labirinto;

	labirinto = str_map2bin_map(mapa,n_linhas,&dim_labirinto);
	//printMapa(labirinto,dim_labirinto); pera();
	reduced_labirinto = get_reduced_map(labirinto,dim_labirinto,&dim_reduced_labirinto);
	//printMapa(reduced_labirinto,dim_reduced_labirinto); pera();
	reduced_labirinto = preProcessing(reduced_labirinto,dim_reduced_labirinto);
	//printMapa(reduced_labirinto,dim_reduced_labirinto); pera();

	//conta a quantidade de lugares validos, lugares permitidos para caminhar
	int lugares_validos = get_number_of_valid_space(reduced_labirinto,dim_reduced_labirinto);

	CImg<int> cimg_labirinto(dim_reduced_labirinto.y,dim_reduced_labirinto.x,1,3);
	Point nest,food; food.x = 111111; food.y = 111111;
	cimg_forXY(cimg_labirinto,x,y){
		if(reduced_labirinto[x][y] == 1) //wall
			cimg_labirinto(y,x,0) = cimg_labirinto(y,x,1) = cimg_labirinto(y,x,2) = 0;
		else if(reduced_labirinto[x][y] == 0)//free space
			cimg_labirinto(y,x,0) = cimg_labirinto(y,x,1) = cimg_labirinto(y,x,2) = 255;
		else if(reduced_labirinto[x][y] == 5){//nest
			cimg_labirinto(y,x,0) = 255;
			cimg_labirinto(y,x,1) = 0;
			cimg_labirinto(y,x,2) = 0;
			nest.x = x; nest.y = y;
		}
		else if(reduced_labirinto[x][y] == 3){//food
			cimg_labirinto(y,x,0) = 0;
			cimg_labirinto(y,x,1) = 255;
			cimg_labirinto(y,x,2) = 0;
			food.x = x; food.y = y;
		}
		else{
		}

	}

	//pherom matrix
	double **PheromMatrix;
	PheromMatrix = new double*[dim_reduced_labirinto.x];
	for(int i=0;i < dim_reduced_labirinto.x;i++)
		PheromMatrix[i] = new double[dim_reduced_labirinto.y];

	//initializing
	for(int i=0;i<dim_reduced_labirinto.x;i++)
		for(int j=0;j<dim_reduced_labirinto.y;j++)
			PheromMatrix[i][j] = 0;

	//initializing ants
	Ant *formigas;
	formigas = new Ant[num_formigas];
	double distance_nest_food = euclidian_distance_2D(nest,food);
	//ant_energy += distance_nest_food + dim_reduced_labirinto.x*dim_reduced_labirinto.y;


	for(int i=0;i<num_formigas;i++){
		formigas[i].set_nest_position(nest);
		formigas[i].set_position(nest); //colocando as formigas no ninho
		formigas[i].set_id(i);
		formigas[i].set_ant_pherom_energy(ant_pherom_energy);
		formigas[i].set_ant_energy(ant_energy);
		formigas[i].set_ant_memory(ant_memory);
		formigas[i].set_parametro_prob_repeat_place(parametro1_prob,parametro2_prob);
	}


	//opcoes do display
	CImgDisplay main_disp(cimg_labirinto,"Labirinto - ACO",0);
	CImgDisplay ferom_disp(cimg_labirinto,"Feromonio",0);
	CImg<double> ferom = cimg_labirinto;
	cout << "\nDimensao do Labirinto Reduzido: " << dim_reduced_labirinto.x << " x " << dim_reduced_labirinto.y << "\n";
	cout << "\nPosicao do Ninho: " << nest.x << "," << nest.y;
	cout << "\nPosicao do Alimento: " << food.x << "," << food.y;
	cout << "\nA Distancia Euclidiana entre o Ninho e o Alimento e: " << distance_nest_food;
	cout << "\nA quantidade de lugares validos sao: " << lugares_validos;
	int aumento_x = 1,aumento_y = 1,proporcao_de_aumento_x = 1,proporcao_de_aumento_y = 1,fator_de_aumento = aumento_display;
	if(aumento_display > 0){
			aumento_x = dim_reduced_labirinto.x * fator_de_aumento;
			aumento_y = dim_reduced_labirinto.y * fator_de_aumento;
			main_disp.resize(aumento_x,aumento_y);
			ferom_disp.resize(aumento_x,aumento_y);
			proporcao_de_aumento_x = aumento_x/dim_reduced_labirinto.x;
			proporcao_de_aumento_y = aumento_y/dim_reduced_labirinto.y;
	}


	//display e laco principal
	int it = 0;//index_best = 0;
	while(!main_disp.is_closed()){//&& it < num_iteracoes){ //cimg_labirinto.display("non_disp");
		//walk ants walk!!

		for(int i=0;i<num_formigas;i++)
			formigas[i].Walk(PheromMatrix,reduced_labirinto,dim_reduced_labirinto);


		cout << "\nIteracao: " << it <<"\tSmallest Path: " << getSmallestPath(formigas,num_formigas) << "\tAverage: " << getAverage(formigas,num_formigas)
			 << "\tVariance: " << getVariance(formigas,num_formigas) << "\tStandard Deviation: " << getStdDev(formigas,num_formigas);

		cimg_labirinto = update_display(formigas,num_formigas,PheromMatrix,taxa_evaporacao,reduced_labirinto,dim_reduced_labirinto,cimg_labirinto);

		cimg_forXY(ferom,x,y){
			ferom(y,x,0) = ferom(y,x,1) = ferom(y,x,2) = PheromMatrix[x][y] * 255;
		}


		main_disp.wait(refresh_time);
		//main_disp.wait();
		cimg_labirinto.display(main_disp);

		if(ferom_disp.button()){
			//cout << "\n(" << (int)(ferom_disp.mouse_x()/proporcao_de_aumento_x) << "," << (int)(ferom_disp.mouse_y()/proporcao_de_aumento_y) << ")\n";
				PheromMatrix[(int)(ferom_disp.mouse_y()/proporcao_de_aumento_y)][(int)(ferom_disp.mouse_x()/proporcao_de_aumento_x)] = 1;
		}
		ferom.display(ferom_disp);
		it++;

		//printPheromMapa(PheromMatrix,dim_reduced_labirinto);

	}

	fclose(arquivo);
	//liberando memoria...
	if(mapa) delete[] mapa;

	for(int i=0;i<dim_reduced_labirinto.x;i++)
		delete[] reduced_labirinto[i];


	delete[] reduced_labirinto;

	for(int i=0;i<dim_labirinto.x;i++)
		delete[] labirinto[i];
	delete[] labirinto;

	delete[] formigas;


	cout << "\nit works!\n";

	return 0;
}
//-------------------------------------------------------------------------------------------------------------


